Skip to content

Fix trace-water phase split causing pump PS-flash failures - #4320

Merged
EvenSol merged 8 commits into
masterfrom
fix/trace-water-aqueous-psflash-recovery
Oct 15, 2026
Merged

EvenSol merged 8 commits into
masterfrom
fix/trace-water-aqueous-psflash-recovery

Conversation

@Sviatose

@Sviatose Sviatose commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Master integration — 2026-10-15

Integrated head 8d271e2f014b024628a9675d1de299bd9d792756 incorporates master 1798b61092aa356297402af02fff9d9e98749e5a, including the merged shared mixing and cold-wax fixes. All source changes combined cleanly; regenerated engineering coverage resolves the integration conflict. The combined tree is 403496f3b981ddda11b7d6184a49e4077d255949.

Validation: 41 focused tests passed, zero failures/errors/skips, covering named cold wax, trace-water recovery, flow-rate inventory, wells/reservoir and deterministic mixing accumulators. Stable direct formatting, both hook stages, documentation search, coverage generation/check and whitespace checks passed. Documentation impact: existing guides and task-log entries from both branches are retained; this integration introduces no new API or numerical tolerance changes. Earlier validation below applies to its recorded checkpoints. Publication and merge follow the user's explicit instruction to integrate in dependency order; final-head CI is tracked separately.


Coordination and merge gates — 2026-10-14

Reviewed published head: 0915f16e49e992d6c103618ad706d67fce20012e.

All current-head checks now pass (38 successful check runs and one skip). The master integration retains all nine original Java files byte-for-byte and carries the bounded dependency-resolution retry in the canonical renamed workflow. The two prior inline review threads are resolved; comments are not approvals.

The actual patch spans thermodynamics (TPflash, SystemThermo) and process simulation (WellSystem), so it requires an accepted NRC decision, covered by the proposed #4331 record, plus affected-owner and independent flash/process-domain review. Independent maintainer coverage must address observable failure and inventory contracts. These review roles need not mean three separate people; no applicable approval is currently recorded.

Integration: After #4329 and before general #4306 integration. The isolated master+#4329+#4320 numerical trial passed 165 tests across 21 classes, with zero failures/errors/skips on JDK17; this does not replace final-head CI or performance qualification.


Original implementation and earlier validation history

Java 8 CI recovery — 2026-10-14

Updated at 402db73e7a99c18d849f192ae69224a22a01f162. VALIDATION PENDING CI on this exact head.

The failed Java 8 model-specification job stopped before compilation: Maven could not resolve maven-resources-plugin:3.3.1 from Central. The missing evidence/summary errors followed because no tests ran. The published plugin supports Java 8 (Apache requirements).

The model-specification workflow now uses -U to recheck missing artifacts and makes one additional attempt after a ten-second delay, only for Maven's terminal PluginResolutionException or DependencyResolutionException diagnostics. Compilation errors, failed assertions, missing tests and fork failures remain fatal without retry. Persistent resolution failures preserve a nonzero exit status. Test selection and required evidence checks are unchanged. This is a CI-only change; no numerical code or calculation performance is affected.

Validation:

  • 11 controlled shell checks: reproduced the original one-attempt failure, then checked recovery, persistent resolution failure, compilation/test/missing-test/fork failures, and failure after the retry. All passed.
  • YAML parse and Bash syntax checks passed.
  • ./mvnw -B -U -ntp -f pomJava8.xml org.apache.maven.plugins:maven-resources-plugin:3.3.1:help: passed on local OpenJDK 17. This is plugin validation, not a Java 8 runtime test.
  • python3 devtools/run_spotless.py apply, python3 devtools/run_spotless.py check, python3 devtools/check_documentation_search.py, pre-commit run --all-files --hook-stage pre-commit, pre-commit run --all-files --hook-stage pre-push, and git diff --check: all exited 0 after the NeqSim Maven bootstrap.
  • Documentation impact: CI recovery policy recorded in the workflow and task log; no simulation/API guide changes are needed.

Latest CI regression repair — 2026-10-14

Updated at 15d1b8defe7cff39899ff8592f8ed75baebf5e2f. Local validation passed; GitHub CI is pending on this exact head. The ten failing cases from the previous head were reproduced; all 76 tests in the seven affected classes pass on exact master b0de5165.

The broad regression came from rebuilding component inventories on every flow change. This repair retains the established arithmetic for upward and same-rate changes, and for downward changes whose prospective component and total inventories remain accurate to 1e-12 relative. It reconstructs positive normalized inventories only when a downward difference update loses that accuracy. The existing extreme inventory and trace-component protection remains covered.

Two-fluid VLP now keeps its cloned reference inventory while reading intensive properties, applying the requested rate in the existing velocity calculation. This avoids the unnecessary init(0) and init(1) performed by the removed setter call and prevents a failed flash at positive near-zero trial rates. Tests cover near-zero continuity, reference-flow magnitude invariance and source preservation.

The reservoir reference is updated with physical evidence, keeping the existing 0.1 bar tolerance: the old setter produced 1,260,096.07887 kg/hr oil for a requested 1,259,689.92248 kg/hr (0.0322% excess). The corrected rate gives 355.175066 bara after ten years. The test now checks all four requested stream rates, each component balance and constant volume at every annual step. An independent audit found maximum relative component imbalance 1.50e-16 and relative volume error below 1.5e-12.

Validation

  • Expanded Maven run: 607 reported, 605 passed, two pre-existing disabled, zero failures/errors. Slow-test exclusions were disabled. Covers the seven CI-failing classes plus the existing flash, pump, inventory, network and reservoir qualification.
  • All five changed Java source/test files compile with --release 8; Maven build and tests run on OpenJDK 17.0.20. This is not a Java 8 runtime test.
  • Direct Spotless apply/check, both pre-commit/pre-push stages, documentation search (788 Markdown + one HTML), generated engineering coverage, whitespace checks and Javadocs pass.
  • Paired setter and warmed process timings were variable and do not establish a speedup or a regression. Process allocation was effectively unchanged. The deterministic VLP improvement removes two unnecessary initialization calls per positive trial evaluation; the flow guard adds no clone, temporary array or EOS initialization.
  • The earlier trace-water candidate-transfer optimization and strict physical acceptance gates remain unchanged.

This PR remains independent of #4306. Its separate clone/root-selection foundations are related work, not included here. Neither PR is merged by this update. No private-plant convergence or global stability claim is made.

Previous repair and qualification

Latest repair — 2026-10-14

Updated at 2b67ee03629bf845687d14e8a985be982fd4f8a0 in response to the maintainer's request to fix this PR and preserve speed. VALIDATION PENDING CI on this new head. The original CI failure was reproduced and repaired.

Root cause and changes

The public reservoir-to-producer path scales a large inventory down to a small flow. SystemThermo.setTotalFlowRate subtracted almost equal large component amounts, losing significant digits. A later TP flash then received overall fractions that did not sum to one. Twenty beta solves stagnated at a material residual of 9.076572593524901e-8; changing PS tolerances cannot fix this feed.

  • Rebuild nonempty flow inventories from positive normalized component amounts, preserving trace components without extra EOS initialization or a system clone.
  • Transfer a qualified trace-water gas/aqueous repair directly. Only the actual water-rich ordinary fallback is replayed; the trace-water candidate is no longer discarded.
  • Stop non-finite or stalled repair iterations early. Keep the existing 20-call upper bound and strict 1e-12 normalization / 1e-10 material / 1e-8 log-fugacity gates, including final failure reporting.
  • Add a public 250 bara/363.15 K reservoir → extracted gas → rate changes → repeated TP-flash regression. Add scaling tests across 1e-12–1e18 mol, retaining a 1e-10 nitrogen fraction, and a counted gas/aqueous candidate-transfer regression.
  • Update the flow-setting and TP-flash guides, task log and the two affected engineering-inventory digests.

Verified validation

  • Exact old-head ReservoirSurveillanceTest#testHistoryRecorded reproduced the CI failure.
  • The two updated/new regression classes have 7 failing tests on the old production classes. They pass on the candidate.
  • Full local Maven compilation and 530 selected tests: zero failures, zero errors, two pre-existing disabled tests after integrating current master b0de5165, with slow-test exclusions disabled. Includes every previously failing reservoir/well/field class, TP/PS/PH, SRK/PR/CPA, pumps, sour gas, sulfur phase extraction, flow units, inventory and lifecycle tests.
  • Changed Java source/test files also compile with --release 8; execution and the repository's Maven build used OpenJDK 17.0.20. The current repository POM targets Java 17. This does not claim a Java 8 runtime test.
  • Direct Spotless apply/check, documentation-search audit (788 Markdown + 1 HTML), engineering coverage check, both pre-commit/pre-push stages and whitespace checks pass.
  • Javadocs pass using the installed JDK's Javadoc module through a runtime-only launcher because this environment lacks the javadoc executable.
  • The accepted gas/aqueous repair needs one candidate clone and one live initialization; the previous path also launched an ordinary reflash. Balanced endpoint guards remain clone-free and initialization-free.

Three fresh-JVM pairs of an injected gas/aqueous recovery diagnostic show 39–53% less thread CPU time (median 48%) and 46% less allocation (71,184 → 38,544 bytes per recovery), with all 42 endpoint snapshots passing. This diagnostic times the explicitly induced private repair, not a complete public flash. Complete public-flash comparison: 720/720 valid snapshots, every paired state identical; family median CPU changes −4.0% to +4.6%, allocation unchanged. Changed-state process checks pass; after 100 warmups the recycle elapsed-time differences range from −1.93% to +0.88%. Full methods, timing limits and reproduction commands are recorded in the final validation comment. Incorrect old reservoir outputs are not counted as a speed baseline.

This fix remains independent of #4306. Its flow setter is not touched by that PR. Current master was merged to resolve generated-coverage conflicts, and its network/well-flow/wax changes were included in the expanded validation. #4306's separate blockers are not certified by this validation. Full private-plant convergence, experimental accuracy and global stability are not claimed.

Original implementation and earlier qualification

What was the bug?

For a fluid containing mostly oil and a little water, the TP flash could occasionally return the wrong amounts of oil and water. The resulting phase split did not conserve the original fluid composition.

An extremely small temperature change could therefore produce a large, artificial entropy jump. The pump's PS flash was trying to find a temperature with the required entropy, but this jump prevented convergence and caused a temperature-resolution error.

The existing trace-water recovery skipped states that already had an aqueous phase. Its refinement also expected a new phase to appear, so it could not repair the amounts of two phases that were already present.

What changed?

  • Check existing trace-water aqueous endpoints for material-balance errors.
  • For an invalid two-phase aqueous split, re-solve the existing phase fractions from a 50/50 starting guess, with a bounded iteration count.
  • Accept the repaired result through strict local material-balance (1e-10), normalization (1e-12) and fugacity (1e-8) checks, together with the existing candidate-selection checks. The 50/50 split is just a numerical starting point, not the final physical result.
  • Keep PS-flash tolerances unchanged.
  • Add a synthetic heptane/water regression checking material balance, fugacity, entropy and Gibbs energy.
  • Regenerate the required engineering inventory.

Validation

  • All 30 focused flash and pump tests passed again after rebasing onto current master. Spotless formatting checks passed. JaCoCo reporting was skipped for this focused run.
  • The original failing pump snapshot was replayed successfully with the corrected local JAR, including entropy, mass and energy closure.
  • Full-plant convergence has not been verified.
  • No private process snapshots or company fluid data are included in this PR.

Review follow-up implementation (645e5e6)

  • Respect forced-phase, phase-lock and solid-check exclusions for the new existing-aqueous repair.
  • Keep the 20-complete-solve budget explicit; reject non-finite solve corrections and remove the duplicate EOS initialization after solveBeta().
  • Validate the final public return after an attempted repair, including any subsequent topology changes. An unrecovered endpoint now throws an informative IllegalStateException instead of returning silently. Ordinary TP/PS tolerances are unchanged.
  • Add public SRK/PR/CPA temperature/pressure/reuse and actual pump entropy, mass and energy closure tests, plus failure-injection and zero-clone/zero-initialization guard checks.
  • Expand the complete-flash benchmark with trace-water, undersaturated and three-phase families, CPU/elapsed/p95 and allocation measurements. Update the algorithm guide, task log and generated engineering inventory.

Follow-up validation

  • 130/130 focused tests pass on this branch; 130/130 also pass with the targeted [AI-Assisted] refactor(flash): general constrained equilibrium and owned state #4306 lifecycle fixes at 518867a applied in an isolated combined build.
  • Java 8-language compilation used ECJ and execution used Java 17 with verified baseline compiled dependencies. This is not a claim of a full local Maven/JDK build; the new GitHub build/test/Javadoc matrix is running.
  • Direct Spotless apply/check, documentation-search audit, generated coverage, diff checks, and both pre-commit/pre-push stages pass.
  • The public synthetic cases qualify lifecycle behavior. The original author's private-snapshot replay above was not independently repeated during this follow-up; no private snapshot was available. Full-plant convergence and global stability are not certified by these checks.
  • [AI-Assisted] refactor(flash): general constrained equilibrium and owned state #4306 is a separate prerequisite-quality concern: its targeted fixes pass their focused suite, but sour-gas, wet-sulfur and scrubber failures remain documented merge blockers there. Neither PR has been merged.

@Sviatose
Sviatose requested a review from EvenSol as a code owner October 13, 2026 10:09
Copilot AI balanced review requested due to automatic review settings October 13, 2026 10:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Non-finite residuals can bypass repair, and the regression test bypasses the public flash path.

2 open findings
What changed in this PR

Fixes trace-water phase-split recovery to prevent non-conservative states from breaking PS flashes.

Changes:

  • Re-solves invalid aqueous phase fractions from a bounded 50/50 seed.
  • Adds thermodynamic regression coverage.
  • Regenerates engineering coverage metadata.
File Description
TPflash.java Adds aqueous endpoint recovery logic.
TPflashWaterBearingPhaseCollapseRecoveryTest.java Tests material and thermodynamic closure.
engineering-coverage.json Updates the TPflash source digest.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

EvenSol commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator

Review of c4e6d0b335d4a6e01a1194379d0f90146c477834, including the relationship to #4306 and the flash/performance roadmaps.

Recommendation: retain this focused repair, but request stronger public-path and performance qualification before merging. It repairs a real non-conservative endpoint without weakening PS tolerances. It is not yet evidence that the upstream phase-lifecycle cause is fixed, or that all flash workloads retain their speed.

What I verified

The six workflow groups for this head are successful. I inspected the build jobs: Java 8/21 on Ubuntu/Windows, four slow-test shards, model specifications and Javadocs passed (run).

I also compiled the exact base/head TPflash source with Java-8 language compatibility and ran isolated diagnostics on Java 17.0.20 against the same compiled NeqSim core. The base source blob matches GitHub; all 3,534 production Java files in the supporting source JAR match the local source, and connector comparison confirms that TPflash is the only production Java difference between that source and this head. These are focused diagnostics, not an independent full Maven build or reproduction of the private plant snapshot.

For the public synthetic heptane/water case at 307 K, 10.5 bara and 0.003 water mole fraction, after imposing the test's corrupted betas:

Direct recovery diagnostic Base maximum component residual PR maximum component residual
SRK / mixing rule 2 0.996999600227 1.99e-14
PR / mixing rule 2 0.996999600217 2.25e-14
SRK-CPA / mixing rule 10 0.996999333005 2.35e-14

On the repaired states, maximum log-fugacity residual is below 1.31e-11; entropy and Gibbs energy return to the independently flashed reference within about 3.7e-12 J/K and 7.9e-11 J respectively. The 50/50 split is correctly used as a numerical seed. solveBeta() also updates compositions and fugacities; this is not simply overwriting beta and accepting it.

Corrections to the automated review

  1. The reported NaN bypass is not demonstrated by this implementation. maximumComponentMaterialBalanceResidual() (lines 3882–3903) explicitly returns positive infinity for non-finite feed fractions, betas, compositions or accumulated inventory. Infinity passes the repair trigger and fails the acceptance comparison. Injecting NaN into the feed produced residual=Infinity, accepted=false. An explicit finite check could clarify the contract, but should not be described as fixing an established NaN bypass here. Keep the distinction from [AI-Assisted] fix(flash): preserve non-finite stability recovery #4174: a recoverable non-finite intermediate stability-monitor signal is different from accepting a non-finite final equilibrium.

  2. The reflective test does exercise the new trigger. It calls rescueWaterRichEndpoint(), which calls shouldRefineTraceWaterAqueousEndpoint(). I removed only the new trigger in a diagnostic build, leaving the repair loop intact: the SRK residual stayed at 0.996999600227. Thus the statement that deleting the trigger would leave this regression passing is incorrect. The valid remaining concern is coverage of the complete public cleanup/return sequence.

Changes/qualification requested for this PR

1. Add a genuinely failing public TP → PS/pump reproducer. Keep the useful private regression. Merely replacing its reflective invocation with ThermodynamicOperations.TPflash() is insufficient: my public rerun of the same corrupted-beta case already passes on the base for SRK, PR and CPA, with component residuals around 2e-14 and unchanged entropy/Gibbs energy. Reduce the original failure to a public/synthetic fluid and state sequence that fails without this fix. Include the small-temperature scan, cold/reused operation paths, pressure change, entropy closure and pump mass/energy closure. Prove the test fails when this repair is removed. The private snapshot replay reported in the description is useful author evidence, but cannot substitute for a reproducible repository regression.

2. Distinguish a conservative phase split from a stable phase set. The new branch (lines 2285–2298) repairs the existing two phases and returns before stabilityAnalysis(). When the reference is non-conservative, shouldAcceptWaterRichCandidate() intentionally skips comparing its Gibbs energy and accepts a balanced equilibrium. Avoid comparing Gibbs energies at different inventories—that bypass is appropriate—but document that balance plus fugacity equality does not prove no third phase/lower root exists. Qualify nearby bubble/dew/three-phase points against an independent cold/stability path. If necessary, revalidate stability only after this rare invalid-endpoint repair, not on every ordinary flash. This is a qualification risk, not a claim that the supplied binary case is metastable.

3. Bound actual recovery work and make exhausted repair observable. Twenty calls to solveBeta() are not twenty Newton steps: each call can perform up to 49 internal updates, with EOS initialization inside the loop. Its normal return already follows system.init(1), so the outer candidate.init(1) appears redundant on successful iterations; verify failure paths before removing it. Record actual beta iterations, EOS init calls, clones and recovery counts. Stop on validated convergence, detect stalled/failed progress, and provide a bounded fallback or explicit failure when a known non-conservative result cannot be repaired. The current recovery caller can otherwise retain the invalid incumbent after rejection. This is an inherited acceptance/diagnostic gap exposed by the expanded repair scope.

4. Clarify tolerances and documentation. The reused production material-balance threshold is 1e-8, whereas the new test asserts 1e-10 and #2937 targets ordinary component balance at least that tight. Passing one test does not make the production gate a 1e-10 guarantee. Qualify and document the intended case-specific absolute/relative closure; do not globally tighten every solver iteration. Update the private Javadocs: they currently describe a gas/oil candidate, phase appearance/removal and a return requiring removal of one phase, which no longer describes this new branch. Add the documentation-impact assessment and a short update to the flash algorithm guide.

Speed evidence and required gate

The normal new cost for an already balanced trace-water aqueous endpoint is a component-balance scan; it does not launch the cloned repair. Preserve that property.

I ran three alternating base/head fresh-JVM pairs for complete repeated heptane/water flashes: Java 17.0.20, 256 MiB fixed heap, SerialGC, -Xbatch, 1,000 warmups, five batches of 256 flashes per case. Per-fork median thread CPU, microseconds/flash:

Case Base forks PR forks Median paired change
SRK oil/aqueous 110.615 / 109.941 / 98.438 108.044 / 111.844 / 104.030 +1.73%
PR oil/aqueous 115.828 / 110.491 / 107.269 111.835 / 97.828 / 103.733 -3.45%

SRK allocation was unchanged at about 65,000 bytes/flash; PR was about 64,648 → 64,568. Post-batch balance/fugacity diagnostics stayed equal at the recorded precision. These small shared-runner diagnostics establish neither universal slowdown nor universal speedup. CPA recovery was checked numerically; CPA timing was not qualified.

Before declaring speed maintained, use paired full-flash and representative pump/process benchmarks covering dry gas, gas/oil, undersaturated trace water, established oil/aqueous, three phases, CPA and changed-state/recycle workloads. Report cold/warm/nearby-state time, allocation, recovery frequency and tail latency. Separate unchanged valid workloads from previously invalid outputs: an incorrect “fast” flash is not an acceptable performance baseline. Keep timing out of ordinary pass/fail unit tests.

Relationship to other flash work

Work Relationship and recommended handling
#4306 / #4310 Foundational empty-clone inventory, phase removal/reuse and logical/physical slot fixes. #4320 still contains the old removal implementation and does not repair slot aliasing. Instrument where the first inventory/topology error occurs; test whether the lifecycle repair prevents this exact failure before relying on terminal recovery. Do not assume a common root cause without replaying the original sequence.
#4306 / #4311 Avoids expensive rejected aqueous recovery for qualified undersaturated feeds. Preserve the model-qualified mixed-phase screen; no arbitrary water cutoff or pure-water-only stability shortcut. Its reported ~15.8× result is versus an earlier #4306 head, not universal performance parity against master.
#3945 / #3943, #3957 / #3955 (merged) Earlier water-rich phase flips and lost-aqueous recovery causing pump PS failures. #4320 addresses incorrect amounts in an already-present trace-water aqueous split. Run their public temperature-scan, saved-state, pump and phase-appearance regressions together.
#4205 / #4202 (merged) Dry-condensate cleanup conservation repair. Useful pattern: event-gated work, conserved-feed reference, strict candidate qualification and explicit failed recovery. Reuse the contract where applicable; retain the distinct aqueous/model safeguards.
#4174, #4226 (merged) Preserve intermediate recovery semantics while rejecting invalid final/linear-solve results. Retain strict PS/PH closure; increasing iteration limits or relaxing pump tolerances would mask the TP defect.
#3517, #2937, #2939 Reuse the existing full-flash/process benchmark infrastructure. #2937 owns solver stability/equilibrium; #2939 measures full-process impact and attributes repeated flashes/initializations. Avoid parallel fixes of the same recovery path.

Important current dependency status: #4306 head ec8611f is open and its build workflow is failing, despite its earlier local validation. The fast job reports sour-gas consistency, tiny-aqueous seed rejection and sulfur-filter component-balance failures. The slow job reports saturation pressure 105.892988 → 41.005951 bara. Investigate these with balance, fugacity, Gibbs/TPD and model evidence; do not simply relax assertions or change phase expectations. Do not make #4320 depend on an unqualified merge of #4306.

Recommended implementation sequence

  1. Finish this bounded repair with the public failing regression, accurate documentation and no-regression performance evidence. It can remain a separate fix.
  2. Resolve and qualify [AI-Assisted] refactor(flash): general constrained equilibrium and owned state #4306's lifecycle and screening changes, then test the combined source. Measure whether terminal recovery frequency falls; retain a bounded safeguard for residual hard cases.
  3. Under [Campaign] TP-flash stability, multiphase equilibrium and performance roadmap #2937, progressively consolidate the repeated terminal rescue/refinement chain into one explicit contract: conserved feed and valid phase storage → warm-start solve → residual checks → stability/phase-set update when needed → bounded recovery → accepted state or explicit failure. Do this incrementally with frozen regressions, not as an unmeasured rewrite.
  4. Improve speed by eliminating repeated failed trials, duplicate initialization and unnecessary cloning. Use feasible safeguarded phase-fraction solves and switch from robust successive substitution to Newton when appropriate; reuse warm-state/stability information only with clear invalidation on T/P/composition, EOS, mixing-rule and topology changes.

The numerical direction is consistent with Michelsen's stability/phase-split framework (Part I, Part II) and feasible-region safeguarded multiphase Rachford–Rice work (Okuno et al., 2010). Their inner-solver guarantees must not be mistaken for a global stability guarantee for the complete EOS flash.

The goal is fewer invalid states and fewer recovery attempts, while keeping cheap, well-qualified paths cheap—not ever more unconditional stability calculations or looser thermodynamic tolerances.

EvenSol commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator

Implemented the review follow-up in 645e5e6. The original recovery remains narrowly scoped; ordinary TP/PS tolerances are unchanged.

The repair now:

  • excludes forced-phase, phase-locked and solid-check states from the new existing-aqueous path;
  • uses an explicit budget of 20 complete beta solves, rejects non-finite solve corrections, and removes the redundant initialization after solveBeta();
  • requires local material balance 1e-10, beta/composition normalization 1e-12, and log-fugacity closure 1e-8, alongside the existing candidate-selection checks;
  • checks the final endpoint after an attempted repair, allowing subsequent phase topology changes, and throws an informative exception if it remains invalid.

The final check is local numerical qualification, not a global stability certificate. Balanced endpoints incur no extra candidate clone or EOS initialization in the guard.

Validation

130/130 focused tests pass, including public SRK/PR/CPA temperature sweeps, pressure changes and return, reused operations, cold-reference agreement, and actual pump entropy/mass/energy closure. Injected-state tests cover strict acceptance, failure reporting and the cheap guard. The original corrupted-endpoint regression remains.

The same 130 tests pass in an isolated combined build with the targeted #4306 lifecycle fixes at 518867a. Those fixes preserve root selectors and physical phase mappings during rollback, index Newton beta logically, and reject invalid higher-Gibbs aqueous seeds. #4306 has its own 145-test passing focused suite, but its 3 sour, 3 wet-sulfur and 1 scrubber failures remain explicit merge blockers. Slower sour-polishing and broader Newton-objective experiments were excluded.

Java 8-language compilation used ECJ; execution used Java 17 and verified baseline compiled dependencies. This was not a full local Maven/JDK build. Direct Spotless apply/check, documentation search, engineering inventory generation, diff checks, and both pre-commit/pre-push stages pass. The new GitHub build/test/Javadoc matrix remains authoritative.

Performance evidence

Baseline: this PR's previous exact head c4e6d0b; candidate: 645e5e6. Identical harness, Linux Java 17, -Xms256m -Xmx256m -XX:+UseSerialGC -Xbatch, three alternating fresh-JVM pairs.

The complete-flash screen covers 12 families × unchanged/changed states, 5 batches × 32 measured flashes per state, 64 warmups (4096 for the three cheap families). All 720 batch-end numerical snapshots pass; all 360 matched baseline/candidate snapshots are identical. Median allocation differences are at most 4 bytes/flash. An injected SRK repair also produced exactly the same balance/fugacity residuals while reducing counted init(int) calls from 43 to 42.

Short-warmup timing was noisy and showed increases in some CPA/condensate cases. Longer-warmup checks were therefore run on a fixed CPU with 1000 warmups, 5 measured batches, and three alternating JVM pairs:

Workload Measured flashes/batch Median paired CPU change
Trace-water SRK, repeated equilibrium 256 -3.3%
Trace-water PR, repeated equilibrium 256 +0.1%
Multiphase condensate, unchanged 128 -2.2%
Multiphase condensate, changed 128 -10.3%
CPA water, unchanged 256 +0.1%
CPA water, changed 256 -5.5%

The trace-water repeated-equilibrium checks used 1000 warmups and the same JVM flags, but were not CPU-pinned. Individual paired measurements still varied considerably; these are representative measurements, not a universal throughput or tail-latency guarantee. The longer-warmup checks did not reproduce the screen's CPA/condensate slowdown.

The existing large-process harness also passed all 10 measured serial and 10 recycle changed-state samples on both versions (size 4, sequential strategy, 10 warmups). Single-pair median elapsed times were serial 117.0 → 92.2 ms and recycle 311.2 → 318.8 ms; these process timings are diagnostic only.

Complete short-warmup screen (median paired changes; positive means slower)
Case State sequence CPU change p95 change Allocation change
dry-gas unchanged 0.4% 12.1% 0.0 B
dry-gas changed -3.5% -4.4% 0.0 B
hot-gas unchanged 3.9% -0.1% 0.0 B
hot-gas changed 8.6% -11.5% 0.0 B
two-phase unchanged -1.5% -8.0% 0.0 B
two-phase changed 1.4% 38.1% 0.0 B
cpa-water unchanged 11.2% 13.7% 0.0 B
cpa-water changed 29.3% 16.2% 0.0 B
condensate unchanged -1.8% 6.1% 0.0 B
condensate changed 16.9% 22.2% 4.0 B
condensate-cold unchanged 2.1% 16.2% 0.0 B
condensate-cold changed 12.8% 17.8% 1.0 B
condensate-multiphase unchanged 19.4% 17.0% 0.0 B
condensate-multiphase changed 21.5% 26.3% 0.0 B
trace-aqueous-srk unchanged -6.1% -13.3% 0.0 B
trace-aqueous-srk changed 2.6% 56.6% 0.0 B
trace-aqueous-pr unchanged -6.7% 27.9% 0.0 B
trace-aqueous-pr changed 17.2% 38.5% 0.0 B
trace-aqueous-cpa unchanged 2.0% 5.2% 0.0 B
trace-aqueous-cpa changed -9.5% -13.9% 0.0 B
trace-undersaturated unchanged -4.0% -15.8% 0.0 B
trace-undersaturated changed 16.5% 67.1% 0.0 B
trace-three-phase unchanged 4.8% 31.8% 0.0 B
trace-three-phase changed 10.3% 26.2% 0.0 B

This screen is retained including unfavorable measurements; it is not a steady-state speed gate. The expanded TPflashEndpointRecoveryBenchmark accepts output path, flashes/batch, batches, warmups and optional family prefix, and records elapsed/CPU/p95/allocation values plus numerical states. For the longer checks, use 128 5 1000 condensate-multiphase or 256 5 1000 cpa-water with the same JVM flags.

The algorithm guide and task log document the trigger, exclusions, acceptance contract, retry cost and validation limits. The original private process snapshot was unavailable during this follow-up; public cases qualify lifecycle behavior and do not establish full-plant convergence. Neither PR has been merged.

EvenSol commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

Updated this PR in be5d85f, then merged current master (b0de5165) in 2b67ee0 to clear the conflict. GitHub now reports no merge conflicts.

The CI failure was caused by subtractive cancellation in SystemThermo.setTotalFlowRate: reservoir-sized component inventories were subtracted down to a small stream, corrupting the overall composition. The public reservoir regression reproduces the exact old-head repair exception. Rebuilding positive normalized component inventories fixes the feed itself, preserving trace species.

The performance changes retain the qualified gas/aqueous candidate instead of cloning/reflashing it, and stop non-finite or repeatedly stalled repair iterations early. The strict normalization (1e-12), material (1e-10) and log-fugacity (1e-8) acceptance gates, phase exclusions and final failure reporting remain intact. Ordinary TP/PS tolerances are unchanged.

Validation

  • Exact old head 645e5e6: the two regression classes expose seven failures, including the public reservoir path and the duplicate-clone assertion.
  • Final merged source: 530 tests, 0 failures, 0 errors, 2 existing disabled tests (528 passed). All previously failing reservoir, well and field-development cases pass, including the slow lifecycle cases. Added master-side network and wax regressions also pass.
  • Full Maven production/test compilation on OpenJDK 17.0.20; changed Java files also compile with --release 8 (language/API compilation, not a Java 8 runtime claim).
  • Direct Spotless apply/check, documentation audit, engineering-coverage check, pre-commit, pre-push, whitespace checks and Javadoc generation all exit 0. Javadoc needed a runtime-only launcher for the installed JDK module because the executable is absent in this environment.
  • Both existing review threads are addressed and resolved.

Commands, run after the NeqSim Maven bootstrap:

./mvnw -B -ntp test '-Dtest=TPflash*Test,TPmultiflash*Test,PSFlash*Test,PHflash*Test,SystemThermo*Test,PumpTest,PumpSinglePhaseLiquidPSflashTest,ReservoirSurveillanceTest,WellFlowTest,FieldDevelopmentNPVTest,NorwegianOilFieldLifecycleTest,NamedComponentWaxFractionTest,WaxFractionSimTest,FieldNetworkTopologyTest,FieldWellNetworkProcessUnitTest,LoopedPipeNetworkTest' -DexcludedTestGroups= -Djacoco.skip=true
python3 devtools/run_spotless.py apply
python3 devtools/run_spotless.py check
python3 devtools/check_documentation_search.py
python3 devtools/build_engineering_coverage.py --check
pre-commit run --all-files --hook-stage pre-commit
pre-commit run --all-files --hook-stage pre-push
./mvnw -B -ntp javadoc:javadoc -DskipTests

Performance and numerical evidence
To isolate this fix, paired benchmarks compare exact old production classes 645e5e6 with be5d85f; final master integration was validated separately by the 530-test run. Three fresh-JVM pairs, alternating version order, each pair pinned to a CPU; later pairs ran concurrently on separate CPUs. OpenJDK 17.0.20, -Xms256m -Xmx256m -XX:+UseSerialGC -Xbatch.

Measurement Result
Injected gas/aqueous repair diagnostic Median 48.3% less thread CPU (39.4–53.3% across pairs); 45.9% less allocation, 71,184 → 38,544 bytes/repair
Complete public TP flashes 720/720 valid snapshots; every paired state identical, including inventory, phase split and thermodynamic properties
Ordinary/recovery family CPU medians −4.0% to +4.6% across the 12 families; allocation unchanged to measurement precision
Changed-state serial process, 10 warmups / 10 measurements Median elapsed reduction 18.4% (15.5–29.5% across pairs); allocation −0.10%; all 60 snapshots pass
Changed-state recycle process, 100 warmups / 30 measurements Median elapsed +0.65%, range −1.93% to +0.88%; allocation −0.009%; all 180 snapshots pass

The injected diagnostic explicitly sets the equilibrated methane/water gas/aqueous betas to 0.5/0.5 before timing the private repair (1,024 warmups, seven batches of 256 repairs; 42/42 measured endpoint snapshots pass). It measures the removed clone/reflash; it is not a complete public-flash speedup claim. The counted regression independently proves one candidate clone and one live initialization.

The public TP harness covers 12 families × unchanged/changed conditions × 5 batches × 3 pairs, with 32 flashes/batch and 128 warmups (4,096 for the cheap cases). Maximum material residual is 4.68e-12, log-fugacity residual 5.24e-11, phase normalization 3.34e-16, beta normalization zero. Per-family median CPU changes: dry gas +1.1%, hot gas −4.0%, two-phase −1.0%, CPA water +2.6%, condensate +0.6%, cold condensate +1.0%, multiphase condensate −0.3%, trace aqueous SRK +0.3%, PR −1.4%, CPA +1.0%, undersaturated +2.4%, three-phase +4.6%. Individual short-batch outliers occur, so no general flash speedup is claimed.

The initial short-warmup recycle result was noisy (+6.4% median, −1.0% to +26.0%); the longer-warmup check above resolves that uncertainty. Process iteration counts are unchanged, and the harness's existing mass/component/energy criteria all pass. This does not tighten the process recycle tolerance or claim private-plant convergence. Invalid old reservoir results are never counted as a fast baseline.

Reproduce the public harnesses with each version's compiled classpath:

java -Xms256m -Xmx256m -XX:+UseSerialGC -Xbatch -cp "$CP" neqsim.thermodynamicoperations.flashops.TPflashEndpointRecoveryBenchmark /tmp/flash.json 32 5 128
java -Xms256m -Xmx256m -XX:+UseSerialGC -Xbatch -cp "$CP" neqsim.process.processmodel.LargeProcessSteadyStateBenchmark serial changed 10 10 4 sequential /tmp/serial.json
java -Xms256m -Xmx256m -XX:+UseSerialGC -Xbatch -cp "$CP" neqsim.process.processmodel.LargeProcessSteadyStateBenchmark recycle changed 100 30 4 sequential /tmp/recycle.json

CI: Exact-head run 38539212902. Model-specification jobs and Java 21 Javadocs have passed; full Ubuntu/Windows and slow-test jobs are still running. Formatting, pre-commit, documentation search, engineering coverage, CodeQL security and code-quality checks have passed. This remains an open PR; it has not been merged.

Retain established arithmetic for ordinary and upward flow updates; rebuild
only downward inventories whose difference update loses accuracy. Avoid
scaling the VLP property basis to near-zero trial flow. Add component and
volume closure evidence for the corrected reservoir production rate.

Validation: 605 selected tests passed, 2 pre-existing disabled, no failures
or errors; Java 8 language compilation and repository hygiene gates pass.

EvenSol commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

Published the CI regression repair at 15d1b8defe7cff39899ff8592f8ed75baebf5e2f, using an expected-head lease against 2b67ee03629bf845687d14e8a985be982fd4f8a0.

The previous head reproduced eight assertion failures and two errors. An independent exact-master run passed all 76 tests in those seven classes, confirming these were branch regressions. The common cause was applying positive inventory reconstruction to every flow update; ordinary floating-point endpoints and empty-stream behavior changed. The repair limits reconstruction to downward updates whose component or total difference update would lose more than 1e-12 relative accuracy. Upward, same-rate and accurate ordinary updates retain their established arithmetic. Extreme 1e-12–1e18 mol scaling and the 1e-10 trace fraction remain tested.

The two-fluid VLP failure had a separate direct cause: scaling its property-only fluid clone to a near-zero trial rate. That scaling is unnecessary because the calculation reads intensive properties and already applies the trial rate to its velocities. Removing the setter call restores positive near-zero continuity and avoids one init(0) plus one init(1) per positive evaluation. New assertions check reference inventories from 100 to 1e6 Sm3/day and verify the source stream is unchanged. The explicit zero-flow boundary is retained.

The reservoir reference change is supported by a material audit, not a widened tolerance. The old oil producer was 0.0322% above its requested rate (1,260,096.07887 versus 1,259,689.92248 kg/hr). Correcting that rate changes the ten-year pressure to 355.175066 bara. The existing 0.1 bar tolerance is retained, and the test now asserts all four stream rates, component inventory against integrated production/injection, and constant volume at every annual step. Independent maximum relative component imbalance was 1.50e-16; relative volume error was below 1.5e-12.

Validation on the published source tree

  • Expanded Maven run: 607 reported = 605 passed + two pre-existing disabled; zero failures/errors, slow exclusions disabled, build successful in 6m35s.
  • Original failing classes all pass: adsorption 23, distillation 26, exchanger 4, reservoir 1, well 15, constraint activity 3, operating action 4.
  • Existing/new inventory cases: 6 pass. Broader coverage includes TP/PS/PH, cubic/CPA/lifecycle/aqueous endpoint cases, pumps, networks, reservoirs and flow units.
  • All five edited Java files compile with --release 8. Maven/test runtime is OpenJDK 17.0.20, not a Java 8 runtime certification.
  • Direct Spotless apply/check; documentation search (788 Markdown + one HTML); engineering inventory generation/check; pre-commit and pre-push hooks; whitespace checks; Javadocs: all pass. Javadocs use the installed JDK module through a runtime-only launcher because the environment has no standalone javadoc executable. No hook bypass.

A focused reproduction command for the affected and inventory suites is:

./mvnw -Dtest=AdsorptionBedTest,DistillationColumnTest,MultiStreamHeatExchangerTest,SimpleReservoirTest,WellSystemTest,ConstraintActivityAnalyzerTest,ProcessModelOperatingActionTest,SystemThermoFlowRateInventoryTest -DexcludedTestGroups= -Djacoco.skip=true test

Performance limits: three fresh-JVM setter pairs and three warmed 10,000-iteration process pairs had substantial timing variability. The warmed process CPU differences ranged from −20.3% to +31.1%; allocation varied by less than 0.05%, and all 60,000 measured process iterations were valid. These measurements do not establish a speedup or a regression. The defensible optimization is removal of the two unnecessary VLP initialization calls; the cancellation guard adds no EOS initialization, clone or temporary array. The earlier trace-water candidate-transfer optimization is unchanged.

Related clone/root-selector problems are being repaired independently in #4306; its changes are not copied into this branch. GitHub CI on this new exact head is pending. No merge performed.

Recheck cached missing artifacts with -U and retry only Maven plugin or
dependency resolution failures. Preserve failing exit codes and fail fast
on compilation, missing-test, and numerical test failures.

Validation: 11 shell behavior checks; plugin resolution probe; Spotless
apply/check, documentation audit, pre-commit and pre-push all passed.
Documentation impact: CI recovery policy recorded in workflow and task log.
Carry the bounded Maven-resolution retry into the renamed core workflow, retain all eight slow-test shards and four historical check contexts, and preserve both task-log entries. Keep all nine original Java source/test files byte-identical and regenerate engineering coverage.

Validation: direct Spotless apply/check, documentation search, engineering coverage check, both pre-commit stages, workflow structure/Bash checks and whitespace checks passed. Isolated current-master+#4329+#4320 integration passed 165 tests across 21 classes with no failures/errors/skips on Java 17. Full exact-head GitHub CI remains pending.
@EvenSol
EvenSol merged commit aa3b281 into master Oct 15, 2026
22 checks passed
@EvenSol
EvenSol deleted the fix/trace-water-aqueous-psflash-recovery branch October 15, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants